home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / x11 / xshowimf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-21  |  41.4 KB  |  1,743 lines  |  [TEXT/KAHL]

  1. /* A no-longer simple X program to display and edit xconq image families.
  2.    Copyright (C) 1994, 1995 Massimo Campostrini & Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* note: display of three-color images (mono+mask) is controlled 
  10.    by the resource "maskColor" (see XShowimf-color.ad) or by the 
  11.    command-line argument "-mc" */
  12.  
  13. #ifndef USE_CONSOLE
  14. #define USE_CONSOLE
  15. #endif
  16.  
  17. #include <stdio.h>
  18. #include <X11/Intrinsic.h>
  19. #include <X11/Xresource.h>
  20. #include <X11/StringDefs.h>
  21. #include <X11/Shell.h>
  22. #include <X11/Xaw/Form.h>
  23. #include <X11/Xaw/Box.h>
  24. #include <X11/Xaw/Label.h>
  25. #include <X11/Xaw/Command.h>
  26. #include <X11/Xaw/Toggle.h>
  27. #include <X11/Xaw/Viewport.h>
  28. #include <X11/cursorfont.h>
  29. #include <bitmaps/check.b>
  30.  
  31. char *strdup();
  32.  
  33. #include "config.h"
  34. #include "misc.h"
  35. #include "lisp.h"
  36. #include "imf.h"
  37. #include "ximf.h"
  38. #include "xutil.h"
  39.  
  40. /* MAXROWS, MAXCOLS, MINROWS, and MINCOLS can be changed safely */
  41. #define MAXROWS 50
  42. #define MAXCOLS 16
  43. #define MINROWS 2
  44. #define MINCOLS 8
  45.  
  46. #define MAXFAMS (MAXROWS*MAXCOLS)
  47.  
  48. extern int numimages;
  49. int numfamilies;
  50. extern ImageFamily **images;
  51. extern char *outdirname;
  52.  
  53. /* the number of allowed magnifications NUMMAGN 
  54.    and the magnification scales magnif can be changed safely */
  55. #define NUMMAGN 4
  56. int magnif[NUMMAGN] = { 1, 2, 4, 6 };
  57.  
  58. typedef enum _prep {
  59.   prep_none,
  60.   prep_delete,
  61.   prep_update,
  62.   prep_export
  63. } Prep_type;
  64.  
  65. typedef struct a_image_stuff {
  66.   int w, h;
  67.   Widget mono_w, mask_w, comb_w, colr_w;
  68.   Pixmap mono_p[NUMMAGN], mask_p[NUMMAGN], comb_p[NUMMAGN], colr_p[NUMMAGN];
  69.   struct a_image_stuff *next;
  70. } ImageStuff;
  71.  
  72. typedef struct a_family_stuff {
  73.   Widget image, name;
  74.   Widget shell, form, label, increase_magn, decrease_magn, close,
  75.          delete, update, export;
  76.   int imagn, changed;
  77.   Prep_type prep;
  78.   ImageStuff *images;
  79. } FamilyStuff;
  80.  
  81. FamilyStuff family[MAXFAMS];
  82.  
  83. int basew=32, baseh=32;
  84.  
  85. int cols, rows, color_comb;
  86.  
  87. Arg tmpargs[10];
  88.  
  89. Widget toplevel;
  90. Widget main_form;
  91. Widget mainviewp;
  92. Widget viewform;
  93. Widget help;
  94. Widget help_shell = NULL;
  95. Widget select_widget;
  96. Widget deselect;
  97. Widget toggle;
  98. Widget read_button;
  99. Widget save_button;
  100. Widget delete;
  101. Widget quit_button;
  102. Widget message;
  103.  
  104. char buffer[200], shortbuf[100];
  105.  
  106. XtAppContext app_con;
  107.  
  108. Display *dpy;
  109. Window rootwin, win;
  110. XrmDatabase xrdb;
  111. Pixel mask_pixel;
  112. Colormap cmap;
  113. int depth, screen;
  114. XVisualInfo vinfo;
  115. Pixmap check;
  116. char *read_suggest="", *write_suggest="";
  117.  
  118. XrmOptionDescRec xoptions[] = {
  119.     { "-geometry",    "*geometry",    XrmoptionSepArg,    NULL },
  120.     { "-xrm",        NULL,        XrmoptionResArg,    NULL }
  121. };
  122.  
  123. static String fallback_resources[] = {
  124. "XShowimf*Command.Font:        -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  125. "XShowimf*Label.Font:        -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
  126. "XShowimf*Toggle.Font:        -adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*",
  127. "XShowimf*increase_magn.Label:    + magn",
  128. "XShowimf*decrease_magn.Label:    - magn",
  129. "XShowimf*mainViewport.height:    484",
  130. "XShowimf*mainForm.?.top:    ChainTop",
  131. "XShowimf*mainForm.?.bottom:    ChainTop",
  132. "XShowimf*mainForm.?.left:    ChainLeft",
  133. "XShowimf*mainForm.?.right:    ChainLeft",
  134. "XShowimf*mainForm.mainViewport.bottom:    ChainBottom",
  135. "XShowimf*mainForm.mainViewport.right:    ChainRight",
  136. "XShowimf*viewForm.?.top:    ChainTop",
  137. "XShowimf*viewForm.?.bottom:    ChainTop",
  138. "XShowimf*viewForm.?.left:    ChainLeft",
  139. "XShowimf*viewForm.?.right:    ChainLeft",
  140. "XShowimf*form.label.width:    310",
  141. "XShowimf*message.width:    365",
  142.  
  143. "XShowimf*helpText.font:    -adobe-times-medium-r-*-*-14-*-*-*-*-*-*-*",
  144. "XShowimf*help.title:        xshowimf help",
  145. "XShowimf*helpDone.Label:    done",
  146. "XShowimf*helpText.label:\
  147. \\    MAIN WINDOW\\n\
  148. help:  if you see this, you know what it does\\n\
  149. select:  select all families\\n\
  150. deselect:  deselect all families\\n\
  151. toggle:  toggle selection\\n\
  152. read:  read a imf/xbm/xpm file\\n\
  153. save:  save selected families to an imf file\\n\
  154. delete:  delete selected families\\n\
  155. quit:  terminate\\n\
  156.  \\n\
  157. family icons:  click to popup a close-up window\\n\
  158. family names:  click to toggle selection\\n\
  159.  \\n\
  160.     CLOSE-UP WINDOWS\\n\
  161. + magn:  increase mgnification\\n\
  162. - magn:  decrease mgnification\\n\
  163. delete:  delete a single image\\n\
  164. update:  re-read an image from disk\\n\
  165. export:  export an image to disk \\n\
  166. close:  pop down this window\\n\
  167.  \\n\
  168. There are no edit buttons (yet); to edit an\\n\
  169. image, export it to disk, edit it with your\\n\
  170. favorite paint program (in the directory\\n\
  171. selected by the \"-o\" option), and update.",
  172.  
  173. "selFile.selFileForm.Font:     -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
  174. "selFile.selFileForm.Label.Font:   -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  175. "selFile.selFileForm.Command.Font: -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  176. "selFile*Scrollbar.thickness:    6",
  177. "selFile*selFilePrompt.height:    30",
  178.   NULL
  179. };
  180.  
  181. void show_image   PROTO ((Widget w, XEvent *event, String *params, Cardinal *num_params));
  182. void show_family  PROTO ((Widget w, XEvent *event, String *params, Cardinal *num_params));
  183. void image_action PROTO ((Widget w, XEvent *event, String *params, Cardinal *num_params));
  184.  
  185. static XtActionsRec  actions[] = {
  186.   { "ShowImage",   show_image   },       /* set info line for this image */
  187.   { "ShowFamily",  show_family  },       /* set info line for family */
  188.   { "ImageAction", image_action },       /* do image stuff */
  189. };
  190.  
  191. /* translation table for label widgets for individual images */
  192. static char Translations[] = 
  193.   "<EnterWindow>:  ShowImage() \n\
  194.    <LeaveWindow>:  ShowFamily() \n\
  195.    <BtnDown>:      ImageAction() ";
  196.  
  197. void do_help();
  198. void done_help();
  199. void do_select();
  200. void do_deselect();
  201. void do_toggle();
  202. void do_read();
  203. void do_save();
  204. void do_delete();
  205. void do_quit();
  206. void do_open_family();
  207. void do_prep_delete();
  208. void do_prep_update();
  209. void do_prep_export();
  210. void do_close_family();
  211. void do_increase_magn();
  212. void do_decrease_magn();
  213.  
  214. void show_image_families PROTO ((int first_time));
  215. void hide_image_families PROTO ((void));
  216.  
  217. void usage PROTO ((void));
  218. void build_name PROTO ((char *name, char *first, char *second));
  219. void display_family PROTO ((int i, int imagn));
  220. void undisplay_family PROTO ((int i));
  221. ImageStuff *init_ims PROTO ((Image *img, Widget parent));
  222. int shell_index PROTO ((Widget w));
  223. Pixmap magnify_bitmap PROTO ((char *data, int h, int w, int s));
  224. Pixmap magnify_colrpix PROTO ((Image *img, int s, Pixel bg_pix, Pixmap mask));
  225. void reset_prep PROTO ((int i));
  226. void set_cursor PROTO ((FamilyStuff *fms, Cursor cursor));
  227. int delete_image PROTO ((Widget w));
  228. int update_or_export_image PROTO ((Widget w, int flag));
  229. Image *find_image PROTO ((ImageFamily *imf, int w, int h));
  230. void destroy_family PROTO ((int i));
  231. void delete_family PROTO ((int i));
  232. int empty_family PROTO ((int i)); 
  233. void mark_changed PROTO ((ImageFamily *imf));
  234.  
  235. /* from libXgnu */
  236. extern FILE *XsraSelFile();
  237.  
  238. int
  239. main(argc, argv)
  240. int argc;
  241. char *argv[];
  242. {
  243.     char *arg, *mask_color_name = NULL;
  244.     char *stype;
  245.     XrmValue val;
  246.     XColor color, junk;
  247.     int i, nfsave;
  248.     
  249.     for (i = 0; i < MAXFAMS; i++) {
  250.     family[i].shell = NULL;
  251.     family[i].changed = FALSE;
  252.     }
  253.     
  254.     init_lisp();
  255.     
  256.     /* toplevel X stuff */
  257.     toplevel =
  258.       XtAppInitialize(&app_con, "XShowimf",
  259.               xoptions, XtNumber(xoptions), &argc, argv,
  260.               fallback_resources, NULL, 0);
  261.     XtAppAddActions(app_con, actions, XtNumber(actions));
  262.     dpy = XtDisplay(toplevel);
  263.     rootwin = DefaultRootWindow(dpy);
  264.     screen = DefaultScreen(dpy);
  265.     
  266.     /* process non-X argument */
  267.     for (i = 1; i < argc; ++i) {
  268.     arg = argv[i];
  269.     if (arg && !strcmp(arg, "-mc")) {
  270.         if (i + 1 < argc) {
  271.         mask_color_name = xmalloc((2 + strlen(argv[i + 1])) * sizeof(char));
  272.         strcpy(mask_color_name, argv[i + 1]);
  273.         argv[i] = NULL;
  274.         argv[i + 1] = NULL;
  275.         } else {
  276.         low_init_error("No color following -mc");
  277.         usage();
  278.         }
  279.     } else if (arg && (!strcmp(arg, "-help") || !strcmp(arg, "--help"))) {
  280.         usage();
  281.     } else if (arg && !strcmp(arg, "-o")) {
  282.         if (i+1 < argc) {
  283.         outdirname = argv[i+1];
  284.         /* Blast the arg because we'll be scanning the args again
  285.            and we want to ignore it then. */
  286.         argv[i] = NULL;
  287.         argv[i+1] = NULL;
  288.         ++i;
  289.         } else {
  290.         low_init_error("No output directory following -o");
  291.         usage();
  292.         }
  293.     }
  294.     }
  295.     
  296.     /* read the files */
  297.     for (i = 1; i < argc; ++i) {
  298.     if (argv[i] != NULL) {
  299.         read_suggest = write_suggest = argv[i];
  300.         /* try to guess the format and read the file */
  301.         if (!read_any_file(argv[i], NULL)) {
  302.         run_warning("Couldn't read \"%s\"", argv[i]);
  303.         }
  304.     }
  305.     }
  306.     
  307.     main_form =
  308.       XtVaCreateManagedWidget("mainform", formWidgetClass, toplevel,
  309.                   NULL);
  310.     
  311.     help =
  312.       XtVaCreateManagedWidget("help", commandWidgetClass, main_form,
  313.                   NULL);
  314.     XtAddCallback (help, XtNcallback, do_help, NULL);
  315.     
  316.     select_widget =
  317.       XtVaCreateManagedWidget("select", commandWidgetClass, main_form,
  318.                   XtNfromHoriz, help,
  319.                   NULL);
  320.     XtAddCallback (select_widget, XtNcallback, do_select, NULL);
  321.     
  322.     deselect =
  323.       XtVaCreateManagedWidget("deselect", commandWidgetClass, main_form,
  324.                   XtNfromHoriz, select_widget,
  325.                   NULL);
  326.     XtAddCallback (deselect, XtNcallback, do_deselect, NULL);
  327.     
  328.     toggle =
  329.       XtVaCreateManagedWidget("toggle", commandWidgetClass, main_form,
  330.                   XtNfromHoriz, deselect,
  331.                   NULL);
  332.     XtAddCallback (toggle, XtNcallback, do_toggle, NULL);
  333.     
  334.     read_button =
  335.       XtVaCreateManagedWidget("read", commandWidgetClass, main_form,
  336.                   XtNfromHoriz, toggle,
  337.                   NULL);
  338.     XtAddCallback (read_button, XtNcallback, do_read, NULL);
  339.     
  340.     save_button =
  341.       XtVaCreateManagedWidget("save", commandWidgetClass, main_form,
  342.                   XtNfromHoriz, read_button,
  343.                   NULL);
  344.     XtAddCallback (save_button, XtNcallback, do_save, NULL);
  345.     
  346.     delete =
  347.       XtVaCreateManagedWidget("delete", commandWidgetClass, main_form,
  348.                   XtNfromHoriz, save_button,
  349.                   NULL);
  350.     XtAddCallback (delete, XtNcallback, do_delete, NULL);
  351.     
  352.     quit_button =
  353.       XtVaCreateManagedWidget("quit", commandWidgetClass, main_form,
  354.                   XtNfromHoriz, delete,
  355.                   NULL);
  356.     XtAddCallback (quit_button, XtNcallback, do_quit, NULL);
  357.     
  358.     message =
  359.       XtVaCreateManagedWidget("message", labelWidgetClass, main_form,
  360.                   XtNfromVert, help,
  361.                   NULL);
  362.     
  363.     nfsave = numfamilies = numimages;
  364.     if (numfamilies>MAXFAMS) {
  365.     numfamilies = MAXFAMS;
  366.     }
  367.     
  368.     mainviewp = XtVaCreateManagedWidget("mainViewport", viewportWidgetClass,
  369.                     main_form,
  370.                     XtNfromVert,   message,
  371.                     XtNallowVert,  True,
  372.                     NULL);
  373.     cols = MINCOLS;
  374.     show_image_families(TRUE);
  375.     
  376.     XtRealizeWidget(toplevel);
  377.     
  378.     /* several of the following functions work only
  379.        after XtRealizeWidget has been called */
  380.     
  381.     sprintf(buffer, "%d image families found", numfamilies);
  382.     if (nfsave>MAXFAMS) {
  383.     sprintf(buffer, "%d image families found, but I can display only %d",
  384.         nfsave, MAXFAMS);
  385.     }
  386.     XtVaSetValues(message, XtNlabel, buffer, NULL);
  387.     
  388.     xrdb = XtDatabase(dpy);
  389.     win = XtWindow(toplevel);
  390.     cmap = XDefaultColormap(dpy,screen);
  391.     depth = DefaultDepth(dpy,screen);
  392.     
  393.     if (XrmGetResource(xrdb, "xshowimf.maskColor",
  394.                "XShowimf.maskColor", &stype, &val)) {
  395.     if (strcmp(stype, "String")) {
  396.         fprintf(stderr, "resource type for maskColor is %s, ignoring\n", stype);
  397.     } else {
  398.         /* command line argument takes precedence over resource */
  399.         if (!mask_color_name) {
  400.         mask_color_name = val.addr;
  401.         }
  402.     }
  403.     }
  404.     
  405.     check = XCreateBitmapFromData(dpy, rootwin, 
  406.                   (char *) check_bits, check_width, check_height);
  407.     
  408.     /* only depths 4, 8, 16, 24, and 32 are supported */
  409.     if ((depth == 4 || depth % 8 == 0) && mask_color_name) {
  410.     color_comb = 1;
  411.     if (XAllocNamedColor(dpy, cmap, mask_color_name, &color, &junk)) {
  412.         mask_pixel = color.pixel;
  413.     } else {
  414.         mask_pixel = XBlackPixel(dpy, screen);
  415.     }
  416.     } else {
  417.     color_comb = 0;
  418.     }
  419.     
  420.     XtAppMainLoop (app_con);
  421.     
  422.     return 0;
  423. }
  424.  
  425. void
  426. show_image_families(first_time)
  427. int first_time;
  428. {
  429.   int i, a, needw, needh, vd, hd;
  430.   static int vert_distance, horiz_distance, maxrows;
  431.   Image *img;
  432.   X11Image *ximg;
  433.   XWindowChanges wchanges;
  434.   Dimension fw, fh, vh, dh, sw;
  435.   Widget dummy, vert;
  436.   static int oldrows = 0, oldcols = 0;
  437.  
  438.   /* pick a number of columns that gives a nice display */
  439.   for (; cols<=MAXCOLS; cols++) {
  440.     if (2*cols*cols>3*numfamilies && cols*MAXROWS>=numfamilies)  break;
  441.   }
  442.   rows = (numfamilies+cols-1)/cols;
  443.   rows = max(rows,MINROWS);
  444.  
  445.   if (first_time) {
  446.     /* 1st pass: create dummy widgets just to get values
  447.        and to estimated needed height and width */
  448.  
  449.     /* be sure we get a vertical scrollbar */
  450.     XtVaGetValues(mainviewp, XtNheight, &vh, NULL);
  451.     viewform = XtVaCreateManagedWidget(
  452.     "viewForm", formWidgetClass, mainviewp,
  453.     XtNheight, 2*vh, NULL);
  454.     dummy = XtVaCreateManagedWidget(
  455.     "dummy", commandWidgetClass, viewform, NULL);
  456.     vert = XtNameToWidget(mainviewp, "vertical");
  457.     if (vert) {
  458.       XtVaGetValues(vert,  XtNwidth, &sw,  NULL);
  459.     } else {
  460.       sw = 10;
  461.     }
  462.     XtVaGetValues(dummy,  XtNvertDistance, &vd,
  463.           XtNhorizDistance, &hd,  XtNheight, &dh,  NULL);
  464.     XtDestroyWidget(dummy);
  465.     vert_distance = baseh+4 + dh+1 + vd;
  466.     horiz_distance = basew+8 + 2+hd;
  467.     maxrows = vh/vert_distance;
  468.     needh = vert_distance*rows + vd;
  469.     needw = horiz_distance*cols + hd;
  470.     if (vh<20)  vh = needh;
  471.     
  472.     XtDestroyWidget(viewform);
  473.     XtVaSetValues(mainviewp,  XtNheight, min(needh,vh),
  474.         XtNwidth, needw+sw+1,  NULL);
  475.  
  476.   /* 2nd pass: create the real widgets */
  477.  
  478.     viewform = XtVaCreateManagedWidget(
  479.     "viewForm", formWidgetClass, mainviewp,
  480.     XtNheight, needh,  XtNwidth, needw+sw+1,  NULL);
  481.   } else {
  482.     /* not first_time */
  483.     if ((rows!=oldrows && (oldrows<maxrows || rows<maxrows)) ||
  484.      cols!=oldcols) {
  485.       /* ask the window manager to change main window size */
  486.       XtVaGetValues(main_form,  XtNheight, &fh,
  487.             XtNwidth,  &fw,  NULL);
  488.       wchanges.width  = fw + (cols-oldcols)*horiz_distance;
  489.       wchanges.height =
  490.       fh + (min(rows,maxrows)-min(oldrows,maxrows))*vert_distance;
  491.       XReconfigureWMWindow(dpy, XtWindow(toplevel), screen, 
  492.                CWWidth | CWHeight, &wchanges);
  493.     }
  494.   }
  495.  
  496.   oldrows = rows;
  497.   oldcols = cols;
  498.  
  499.   /* entries for each image family */
  500.   for (i=0; i<numfamilies; i++) {
  501.  
  502.     /* picture */
  503.     a = 0;
  504.     XtSetArg(tmpargs[a], XtNwidth,  basew+8);  a++;
  505.     XtSetArg(tmpargs[a], XtNheight, baseh+4);  a++;
  506.     XtSetArg(tmpargs[a], XtNlabel, "");  a++;
  507.     x11_interp_imf(dpy, rootwin, images[i], TRUE);
  508.     img = best_image(images[i], basew, baseh);
  509.     if (img->hook != NULL) {
  510.       ximg = (X11Image *) img->hook;
  511.       if (ximg->colr != None) {
  512.     XtSetArg(tmpargs[a], XtNbitmap, ximg->colr);  a++;
  513.       } else if (ximg->mono != None) {
  514.     XtSetArg(tmpargs[a], XtNbitmap, ximg->mono);  a++;
  515.       } else if (ximg->mask != None) {
  516.     XtSetArg(tmpargs[a], XtNbitmap, ximg->mask);  a++;
  517.       }
  518.     }
  519.     if (i>=cols) {
  520.       XtSetArg(tmpargs[a], XtNfromVert,  family[i-cols].name);  a++;
  521.     }
  522.     if (i%cols) {
  523.       XtSetArg(tmpargs[a], XtNfromHoriz, family[i-1].image);  a++;
  524.     }
  525.  
  526.     build_name(buffer, "image_", images[i]->name);
  527.     family[i].image = XtCreateManagedWidget(buffer,
  528.         commandWidgetClass, viewform,
  529.         tmpargs, a);
  530.     XtAddCallback (family[i].image, XtNcallback, do_open_family, NULL);
  531.  
  532.     /* name */
  533.     build_name(buffer, "name_", images[i]->name);
  534.     a = 0;
  535.     XtSetArg(tmpargs[a], XtNwidth, basew+8);  a++;
  536.     XtSetArg(tmpargs[a], XtNlabel, images[i]->name);  a++;
  537.     XtSetArg(tmpargs[a], XtNfromVert, family[i].image);  a++;
  538.     XtSetArg(tmpargs[a], XtNvertDistance, -1);  a++;
  539.     if (i%cols) {
  540.       XtSetArg(tmpargs[a], XtNfromHoriz, family[i-1].name);  a++;
  541.     }
  542.     family[i].name = XtCreateManagedWidget(buffer, 
  543.                        toggleWidgetClass, viewform,
  544.                        tmpargs, a);
  545.   }
  546. }
  547.  
  548. void
  549. hide_image_families()
  550. {
  551.     int i;
  552.  
  553.     /* entries for each image family */
  554.     for (i = 0; i < numfamilies; i++) {
  555.     if (family[i].image)
  556.       XtDestroyWidget(family[i].image);
  557.     if (family[i].name)
  558.       XtDestroyWidget(family[i].name);
  559.   }
  560. }
  561.  
  562. void
  563. usage()
  564. {
  565.     fprintf(stderr,
  566.       "usage: xshowimf [-mc mask color] [-o outdir] imfile ...\n");
  567.     exit(1);
  568. }
  569.  
  570. /* popup a window displaying all the images in the family */
  571.  
  572. void
  573. display_family(i, imagn)
  574. int i;
  575. int imagn;
  576. {
  577.     int img0;
  578.     Widget last, up;
  579.     Image *img;
  580.     FamilyStuff *fms = &family[i];
  581.     ImageStuff *ims;
  582.  
  583.     fms->imagn = imagn;
  584.  
  585.     build_name(buffer, "xshowimf_", images[i]->name);
  586.     fms->shell = XtVaCreatePopupShell(buffer, topLevelShellWidgetClass,
  587.                       toplevel, NULL);
  588.  
  589.     fms->form =
  590.       XtVaCreateManagedWidget("form", formWidgetClass, fms->shell,
  591.                   NULL);
  592.  
  593.     fms->increase_magn =
  594.       XtVaCreateManagedWidget("increase_magn", commandWidgetClass, fms->form, 
  595.                   NULL);
  596.     XtAddCallback (fms->increase_magn, XtNcallback, do_increase_magn, NULL);
  597.  
  598.     fms->decrease_magn =
  599.       XtVaCreateManagedWidget("decrease_magn", commandWidgetClass, fms->form, 
  600.                   XtNfromHoriz, fms->increase_magn,
  601.                   NULL);
  602.     XtAddCallback (fms->decrease_magn, XtNcallback, do_decrease_magn, NULL);
  603.  
  604.     fms->delete =
  605.       XtVaCreateManagedWidget("delete", commandWidgetClass, fms->form, 
  606.                   XtNfromHoriz, fms->decrease_magn,
  607.                   NULL);
  608.     XtAddCallback (fms->delete, XtNcallback, do_prep_delete, NULL);
  609.  
  610.     fms->update =
  611.       XtVaCreateManagedWidget("update", commandWidgetClass, fms->form, 
  612.                   XtNfromHoriz, fms->delete,
  613.                   NULL);
  614.     XtAddCallback (fms->update, XtNcallback, do_prep_update, NULL);
  615.  
  616.     fms->export =
  617.       XtVaCreateManagedWidget("export", commandWidgetClass, fms->form, 
  618.                   XtNfromHoriz, fms->update,
  619.                   NULL);
  620.     XtAddCallback (fms->export, XtNcallback, do_prep_export, NULL);
  621.  
  622.     fms->close =
  623.       XtVaCreateManagedWidget("close", commandWidgetClass, fms->form, 
  624.                   XtNfromHoriz, fms->export,
  625.                   NULL);
  626.     XtAddCallback (fms->close, XtNcallback, do_close_family, NULL);
  627.  
  628.     fms->label =
  629.       XtVaCreateManagedWidget("label", labelWidgetClass, fms->form,
  630.                   XtNfromVert, fms->increase_magn,
  631.                   NULL);
  632.  
  633.     last = fms->label;
  634.     img0 = 1;
  635.     for (img = images[i]->images; img != NULL; img = img->next) {
  636.     if (last)
  637.       up = last;
  638.     last = NULL;
  639.  
  640.     if (img->hook) {
  641.         if (img0) {
  642.         if (!fms->images || fms->changed) {
  643.             fms->images = init_ims(img, fms->form);
  644.         }
  645.         ims = fms->images;
  646.         img0 = 0;
  647.         } else {
  648.         if (!ims->next || fms->changed) {
  649.             ims->next = init_ims(img, fms->form);
  650.         }
  651.         ims = ims->next;
  652.         }
  653.         
  654.         if (ims->mono_p[imagn] != None) {
  655.         sprintf(buffer, "mono-%dx%d", img->w, img->h);
  656.         ims->mono_w = last =
  657.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  658.                       XtNbitmap, ims->mono_p[imagn],
  659.                       XtNfromHoriz, last,
  660.                       XtNfromVert, up,
  661.                       NULL);
  662.         XtOverrideTranslations(ims->mono_w,
  663.                        XtParseTranslationTable(Translations));
  664.         } else {
  665.         ims->mono_w = NULL;
  666.         }
  667.         if (ims->mask_p[imagn] != None) {
  668.         sprintf(buffer, "mask-%dx%d", img->w, img->h);
  669.         ims->mask_w = last =
  670.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  671.                       XtNbitmap, ims->mask_p[imagn],
  672.                       XtNfromHoriz, last,
  673.                       XtNfromVert, up,
  674.                       NULL);
  675.         XtOverrideTranslations(ims->mask_w,
  676.                        XtParseTranslationTable(Translations));
  677.         } else {
  678.         ims->mask_w = NULL;
  679.         }
  680.         if (ims->comb_p[imagn] != None) {
  681.         sprintf(buffer, "comb-%dx%d", img->w, img->h);
  682.         ims->comb_w = last =
  683.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  684.                       XtNbitmap, ims->comb_p[imagn], 
  685.                       XtNfromHoriz, last,
  686.                       XtNfromVert, up,
  687.                       NULL);
  688.         XtOverrideTranslations(ims->comb_w,
  689.                        XtParseTranslationTable(Translations));
  690.         } else {
  691.         ims->comb_w = NULL;
  692.         }
  693.         if (ims->colr_p[imagn] != None) {
  694.         sprintf(buffer, "colr-%dx%d", img->w, img->h);
  695.         ims->colr_w = last =
  696.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  697.                       XtNbitmap, ims->colr_p[imagn],
  698.                       XtNfromHoriz, last,
  699.                       XtNfromVert, up,
  700.                       NULL);
  701.         XtOverrideTranslations(ims->colr_w,
  702.                        XtParseTranslationTable(Translations));
  703.         } else {
  704.         ims->colr_w = NULL;
  705.         }
  706.     }
  707.     }
  708.     fms->changed = FALSE;
  709.  
  710.     /* now show them */
  711.     reset_prep(i);
  712.     XtPopup(fms->shell, XtGrabNone);
  713.     show_family(fms->label, NULL, NULL, NULL);
  714. }
  715.  
  716. void
  717. undisplay_family(i)
  718. int i;
  719. {
  720.     FamilyStuff *fms = &family[i];
  721.     ImageStuff *ims;
  722.  
  723.     if (!fms->shell) {
  724.     return;
  725.     }
  726.  
  727.     XtPopdown(fms->shell);
  728.  
  729.     for (ims = fms->images; ims; ims = ims->next) {
  730.     if (ims->mono_w) {
  731.         XtDestroyWidget(ims->mono_w);
  732.     }
  733.     if (ims->mask_w) {
  734.         XtDestroyWidget(ims->mask_w);
  735.     }
  736.     if (ims->comb_w) {
  737.         XtDestroyWidget(ims->comb_w);
  738.     }
  739.     if (ims->colr_w) {
  740.         XtDestroyWidget(ims->colr_w);
  741.     }
  742.     }
  743.  
  744.     XtDestroyWidget(fms->label);
  745.     XtDestroyWidget(fms->increase_magn);
  746.     XtDestroyWidget(fms->decrease_magn);
  747.     XtDestroyWidget(fms->close);
  748.     XtDestroyWidget(fms->delete);
  749.     XtDestroyWidget(fms->update);
  750.     XtDestroyWidget(fms->export);
  751.     XtDestroyWidget(fms->label);
  752.     XtDestroyWidget(fms->form);
  753.     XtDestroyWidget(fms->shell);
  754.  
  755.     fms->shell = NULL;
  756. }
  757.  
  758. ImageStuff *
  759. init_ims (img, parent)
  760. Image *img;
  761. Widget parent;
  762. {
  763.   int bytesize, j, w, h;
  764.   Pixmap pix;
  765.   X11Image *ximg;
  766.   GC gc;
  767.   ImageStuff *ims;
  768.   char *rawdata;
  769.   Pixel bg_pixel, fg_pixel;
  770.   Widget dummy;
  771.  
  772.   ximg = (X11Image *) img->hook;
  773.   if (!ximg)  return NULL;
  774.  
  775.   ims = (ImageStuff *) xmalloc(sizeof(ImageStuff));
  776.   ims->next = NULL;
  777.   for (j=0; j<NUMMAGN; j++) {
  778.     ims->mono_p[j] = None;
  779.     ims->mask_p[j] = None;
  780.     ims->comb_p[j] = None;
  781.     ims->colr_p[j] = None;
  782.   }
  783.   ims->h = img->h;
  784.   ims->w = img->w;
  785.  
  786.   /* build pixmaps */
  787.   if (ximg->mono != None) {
  788.     ims->mono_p[0] = ximg->mono;
  789.     if (img->rawmonodata) {
  790.       for (j=1; j<NUMMAGN; j++) {
  791.     ims->mono_p[j] =
  792.       magnify_bitmap(img->rawmonodata, img->h, img->w, magnif[j]);
  793.       }
  794.     }
  795.   }
  796.     
  797.   if (ximg->mask != None) {
  798.     ims->mask_p[0] = ximg->mask;
  799.     if (img->rawmaskdata) {
  800.       for (j=1; j<NUMMAGN; j++) {
  801.     ims->mask_p[j] =
  802.       magnify_bitmap(img->rawmaskdata, img->h, img->w, magnif[j]);
  803.       }
  804.     }
  805.   }
  806.  
  807.   if (ximg->mono != None && img->rawmonodata &&
  808.       ximg->mask != None && img->rawmaskdata) {
  809.     bytesize = ((img->w + 7) / 8) * img->h;
  810.  
  811.     if (!color_comb) {
  812.       /* monochrome */
  813.       rawdata = (char *) malloc(bytesize*sizeof(char));
  814.       if (rawdata) {
  815.     for (j=0; j<bytesize; j++) {
  816.       rawdata[j] = img->rawmonodata[j] | ~img->rawmaskdata[j];
  817.     }
  818.     pix = XCreateBitmapFromData(dpy, rootwin, rawdata, img->w, img->h);
  819.     ims->comb_p[0] = pix;
  820.     for (j=1; j<NUMMAGN; j++) {
  821.       ims->comb_p[j] = magnify_bitmap(rawdata, img->h, img->w, magnif[j]);
  822.     }
  823.     free(rawdata);
  824.       }
  825.  
  826.     } else {
  827.       /* color or grayscale */
  828.       /* get pixels; the widget has not been created yet, so we need a dummy */
  829.       sprintf(buffer, "comb-%dx%d", img->w, img->h);
  830.       dummy = XtVaCreateManagedWidget(buffer, labelWidgetClass, parent, NULL); 
  831.       XtVaGetValues(dummy,
  832.             XtNbackground, &bg_pixel,
  833.             XtNforeground, &fg_pixel,
  834.             NULL);
  835.       XtDestroyWidget(dummy);
  836.  
  837.       for (j=0; j<NUMMAGN; j++) {
  838.     w = img->w*magnif[j];
  839.     h = img->h*magnif[j];
  840.     pix = XCreatePixmap(dpy, rootwin, w, h, depth);
  841.     if (!pix || pix==None)  continue;
  842.     gc = XCreateGC(dpy, pix, 0, NULL);
  843.  
  844.     /* background */
  845.     XSetClipOrigin(dpy, gc, 0, 0);
  846.     XSetForeground(dpy, gc, bg_pixel);
  847.     XFillRectangle(dpy, pix, gc, 0, 0, w, h);
  848.  
  849.     /* mask */
  850.     XSetForeground(dpy, gc, mask_pixel);
  851.     XSetClipMask(dpy, gc, ims->mask_p[j]);
  852.     XFillRectangle(dpy, pix, gc, 0, 0, w, h);
  853.  
  854.     /* mono */
  855.     XSetForeground(dpy, gc, fg_pixel);
  856.     XSetClipMask(dpy, gc, ims->mono_p[j]);
  857.     XFillRectangle(dpy, pix, gc, 0, 0, w, h);
  858.  
  859.     ims->comb_p[j] = pix;
  860.       }
  861.     }
  862.   }
  863.   if (ximg->colr != None) {
  864.     /* get bg pixel; the widget has not been created yet, so we need a dummy */
  865.     sprintf(buffer, "colr-%dx%d", img->w, img->h);
  866.     dummy = XtVaCreateManagedWidget(buffer, labelWidgetClass, parent, NULL); 
  867.     XtVaGetValues(dummy,
  868.           XtNbackground, &bg_pixel,
  869.           NULL);
  870.     XtDestroyWidget(dummy);
  871.  
  872.     if (img->rawcolrdata) {
  873.       for (j=0; j<NUMMAGN; j++) {
  874.     ims->colr_p[j] =
  875.       magnify_colrpix(img, magnif[j], bg_pixel, ims->mask_p[j]);
  876.       }
  877.     }
  878.   }
  879.  
  880.   return ims;
  881. }
  882.  
  883. /* build a X-friendly widget name */
  884. void build_name(name, first, second)
  885. char *name, *first, *second;
  886. {
  887.   char *ch;
  888.  
  889.   strcpy(name, first);
  890.   strcat(name, second);
  891.   for (ch=name; *ch; ch++) {
  892.     if (!isalnum(*ch))  *ch = '_';
  893.   }
  894. }
  895.  
  896. void
  897. do_help (w)
  898. Widget w;
  899. {
  900.   Widget w_form, w_done, w_text;
  901.  
  902.   if (!help_shell) {
  903. /* first time: create help popup */
  904.     help_shell = XtVaCreatePopupShell ("help",
  905.                      topLevelShellWidgetClass,
  906.                      toplevel, NULL);
  907.     w_form = XtVaCreateManagedWidget ("helpForm", formWidgetClass,
  908.                       help_shell, NULL);
  909.     w_text = XtVaCreateManagedWidget ("helpText", labelWidgetClass, w_form,
  910.                       NULL);
  911.     w_done = XtVaCreateManagedWidget ("helpDone", commandWidgetClass, w_form,
  912.                       XtNfromVert, w_text,  NULL);
  913.     XtAddCallback (w_done, XtNcallback, done_help, NULL);
  914.   }
  915.   XtPopup (help_shell, XtGrabNone);
  916.   XMapRaised(XtDisplay(help_shell), XtWindow(help_shell));
  917. }
  918.  
  919. void
  920. done_help (w)
  921. Widget w;
  922. {
  923.   XtPopdown (help_shell);
  924. }
  925.  
  926. void
  927. do_select (w)
  928. Widget w;
  929. {
  930.   int i;
  931.  
  932.   for (i=0; i<numfamilies; i++) {
  933.     XtVaSetValues(family[i].name, XtNstate, (Boolean) 1, NULL);
  934.   }
  935. }
  936.  
  937. void
  938. do_deselect (w)
  939. Widget w;
  940. {
  941.   int i;
  942.  
  943.   for (i=0; i<numfamilies; i++) {
  944.     XtVaSetValues(family[i].name, XtNstate, (Boolean) 0, NULL);
  945.   }
  946. }
  947.  
  948. void
  949. do_toggle (w)
  950. Widget w;
  951. {
  952.   int i;
  953.   Boolean state;
  954.  
  955.   for (i=0; i<numfamilies; i++) {
  956.     XtVaGetValues(family[i].name, XtNstate, &state, NULL);
  957.     XtVaSetValues(family[i].name, XtNstate, (Boolean) !state, NULL);
  958.   }
  959. }
  960.  
  961. void
  962. do_read (w)
  963. Widget w;
  964. {
  965.   FILE *stream = NULL;
  966.   char *filename;
  967.   int i, changed;
  968.  
  969.   stream = XsraSelFile(toplevel,         "Read from file:              ",
  970.                "Okay", "Cancel", "Error: can't open file   ",
  971.                read_suggest, "r", NULL, &filename);
  972.   if (!stream)
  973.     return;
  974.  
  975.   read_suggest = filename;
  976.   fclose(stream);
  977.   if (read_any_file(filename, mark_changed)) {
  978.     changed = numimages > numfamilies;
  979.     for (i=0; i<numimages; i++) {
  980.       changed = changed || family[i].changed;
  981.     }
  982.     if (changed) {
  983.       hide_image_families();
  984.       numfamilies = min(numimages,MAXFAMS);
  985.       for (i=0; i<numimages; i++) {
  986.     undisplay_family(i);
  987.       }
  988.       show_image_families(FALSE);
  989.     }
  990.     sprintf(buffer, "successfully read \"%s\"", filename);
  991.   } else {
  992.     sprintf(buffer, "failed reading \"%s\"", filename);
  993.   }
  994.   XtVaSetValues(message, XtNlabel, buffer, NULL);
  995. }
  996.  
  997. void
  998. mark_changed(imf)
  999. ImageFamily *imf;
  1000. {
  1001.   int i;
  1002.  
  1003.   for (i=0; i<numimages; i++) {
  1004.     if (imf==images[i]) {
  1005.       family[i].changed = TRUE;
  1006.       return;
  1007.     }
  1008.   }
  1009. }
  1010.  
  1011. void
  1012. do_save (w)
  1013. Widget w;
  1014. {
  1015.   int i, n;
  1016.   Boolean state;
  1017.   FILE *stream=NULL;
  1018.   char *filename;
  1019.  
  1020.   n = 0;
  1021.   for (i=0; i<numfamilies; i++) {
  1022.     XtVaGetValues(family[i].name, XtNstate, &state, NULL);
  1023.     if (state) {
  1024.       if (!stream) {
  1025.     stream = XsraSelFile(toplevel,         "Save in file:              ",
  1026.                  "Okay", "Cancel", "Error: can't open file   ",
  1027.                  write_suggest, "w", NULL, &filename);
  1028.     if (stream) {
  1029.       write_suggest = filename;
  1030.     } else {
  1031.       return;
  1032.     }
  1033.       }
  1034.       /* write_imf expects rawdata in "natural" order, not in the 
  1035.      X11 "byte-reversed" order the rest of this program uses */
  1036.       reverse_rawdata(images[i]);
  1037.       write_imf(stream, images[i]);
  1038.       reverse_rawdata(images[i]);
  1039.       n++;
  1040.     }
  1041.   }
  1042.   if (stream) {
  1043.     fclose(stream);
  1044.     if (n) {
  1045.       sprintf(buffer, "%d image families saved in \"%s\"",
  1046.           numfamilies, filename);
  1047.       XtVaSetValues(message, XtNlabel, buffer, NULL);
  1048.     }
  1049.   }
  1050. }
  1051.  
  1052. void
  1053. do_delete (w)
  1054. Widget w;
  1055. {
  1056.     delete_family(-1);
  1057. }
  1058.  
  1059. void
  1060. do_quit (w)
  1061. Widget w;
  1062. {
  1063.     exit(0);
  1064. }
  1065.  
  1066. /* callback: popup the family corresponding to the widget 
  1067.          do_open_family was called from */
  1068. void
  1069. do_open_family (w)
  1070. Widget w;
  1071. {
  1072.     int i;
  1073.  
  1074.     for (i = 0; i < numfamilies; i++) {
  1075.     if (w==family[i].image || w==family[i].name) {
  1076.         if (family[i].shell) {
  1077.         if (family[i].changed) {
  1078.             display_family(i, 0);
  1079.         }
  1080.         XMapRaised(XtDisplay(family[i].shell),
  1081.                XtWindow(family[i].shell));
  1082.         } else {
  1083.         display_family(i, 0);
  1084.         }
  1085.         return;
  1086.     }
  1087.     }
  1088. }
  1089.  
  1090. void
  1091. do_prep_delete (w)
  1092. Widget w;
  1093. {
  1094.   int i = shell_index(XtParent(XtParent(w)));
  1095.   FamilyStuff *fms = &family[i];
  1096.  
  1097.   fms->prep = prep_delete;
  1098.   
  1099.   set_cursor(fms, XCreateFontCursor(dpy, XC_pirate));
  1100.   XtVaSetValues(fms->delete, XtNleftBitmap, check, NULL);
  1101.   XtVaSetValues(fms->update, XtNleftBitmap, None, NULL);
  1102.   XtVaSetValues(fms->export, XtNleftBitmap, None, NULL);
  1103. }
  1104.  
  1105. void
  1106. do_prep_update (w)
  1107. Widget w;
  1108. {
  1109.   int i = shell_index(XtParent(XtParent(w)));
  1110.   FamilyStuff *fms = &family[i];
  1111.  
  1112.   fms->prep = prep_update;
  1113.   set_cursor(fms, XCreateFontCursor(dpy, XC_dot));
  1114.   XtVaSetValues(fms->update, XtNleftBitmap, check, NULL);
  1115.   XtVaSetValues(fms->delete, XtNleftBitmap, None, NULL);
  1116.   XtVaSetValues(fms->export, XtNleftBitmap, None, NULL);
  1117. }
  1118.  
  1119. void
  1120. do_prep_export (w)
  1121. Widget w;
  1122. {
  1123.   int i = shell_index(XtParent(XtParent(w)));
  1124.   FamilyStuff *fms = &family[i];
  1125.  
  1126.   fms->prep = prep_export;
  1127.   set_cursor(fms, XCreateFontCursor(dpy, XC_pencil));
  1128.   XtVaSetValues(fms->export, XtNleftBitmap, check, NULL);
  1129.   XtVaSetValues(fms->delete, XtNleftBitmap, None, NULL);
  1130.   XtVaSetValues(fms->update, XtNleftBitmap, None, NULL);
  1131. }
  1132.  
  1133. void
  1134. reset_prep (i)
  1135. int i;
  1136. {
  1137.   FamilyStuff *fms = &family[i];
  1138.  
  1139.   fms->prep = prep_none;
  1140.   set_cursor(fms, XCreateFontCursor(dpy, XC_left_ptr));
  1141.   XtVaSetValues(fms->delete, XtNleftBitmap, None, NULL);
  1142.   XtVaSetValues(fms->update, XtNleftBitmap, None, NULL);
  1143.   XtVaSetValues(fms->export, XtNleftBitmap, None, NULL);
  1144. }
  1145.  
  1146. void
  1147. set_cursor (fms, cursor)
  1148. FamilyStuff *fms;
  1149. Cursor cursor;
  1150. {
  1151.   ImageStuff *ims;
  1152.  
  1153.   for (ims = fms->images; ims; ims = ims->next) {
  1154.     if (ims->mono_w) {
  1155.       XtVaSetValues(ims->mono_w, XtNcursor, cursor, NULL);
  1156.     }
  1157.     if (ims->mask_w) {
  1158.       XtVaSetValues(ims->mask_w, XtNcursor, cursor, NULL);
  1159.     }
  1160.     if (ims->comb_w) {
  1161.       XtVaSetValues(ims->comb_w, XtNcursor, cursor, NULL);
  1162.     }
  1163.     if (ims->colr_w) {
  1164.       XtVaSetValues(ims->colr_w, XtNcursor, cursor, NULL);
  1165.     }
  1166.   }
  1167. }
  1168.  
  1169. void
  1170. do_close_family (w)
  1171. Widget w;
  1172. {
  1173.   int i = shell_index(XtParent(XtParent(w)));
  1174.  
  1175.   if (i>=numfamilies)  return;
  1176.   undisplay_family(i);
  1177. }
  1178.  
  1179. void 
  1180. do_increase_magn (w)
  1181. Widget w;
  1182. {
  1183.   int i = shell_index(XtParent(XtParent(w)));
  1184.   int imag;
  1185.  
  1186.   if (i>=numfamilies)  return;
  1187.  
  1188.   /* already at max? */
  1189.   if ((imag = family[i].imagn+1) >= NUMMAGN)  return;
  1190.   undisplay_family(i);
  1191.   display_family(i, imag);
  1192. }
  1193.  
  1194. void 
  1195. do_decrease_magn (w)
  1196. Widget w;
  1197. {
  1198.   int i = shell_index(XtParent(XtParent(w)));
  1199.   int imag;
  1200.  
  1201.   /* already at min? */
  1202.   if ((imag = family[i].imagn-1) < 0)  return;
  1203.   undisplay_family(i);
  1204.   display_family(i, imag);
  1205. }
  1206.  
  1207. int 
  1208. shell_index(w)
  1209. Widget w;
  1210. {
  1211.   int i;
  1212.  
  1213.   for (i=0; i<numfamilies; i++) {
  1214.     if (family[i].shell==w)  break;
  1215.   }
  1216.  
  1217.   return i;
  1218. }
  1219.  
  1220. Pixmap
  1221. magnify_bitmap (data, h, w, s)
  1222. char *data; 
  1223. int h;
  1224. int w;
  1225. int s;
  1226. {
  1227.   Pixmap pix;
  1228.   int lo, ln, i, j, is, js;
  1229.   char *new, *n, *o, *so, mo, mn;
  1230.  
  1231.   lo = (w + 7) / 8;
  1232.   ln = (w*s + 7) / 8;
  1233.   new = (char *) malloc(s*h*ln*sizeof(char));
  1234.   if (!new)  return None;
  1235.  
  1236.   for (i=0; i<s*h*ln; i++) {
  1237.     new[i] = '\0';
  1238.   }
  1239.  
  1240.   n = new-1;
  1241.   o = data-1;
  1242.  
  1243.   for (i=0; i<h; i++) {
  1244.     so = o;
  1245.     for (is=0; is<s; is++) {
  1246.       o = so;
  1247.       for (j=0; j<w; j++) {
  1248.     if (!(j & 7)) {
  1249.       o++;
  1250.       mo = '\x01';
  1251.     } else {
  1252.       mo <<= 1;
  1253.     }
  1254.     for (js=0; js<s; js++) {
  1255.       if (!((s*j+js) & 7)) {
  1256.         n++;
  1257.         mn = '\x01';
  1258.       } else {
  1259.         mn <<= 1;
  1260.       }
  1261.       if (*o & mo)  *n |= mn;
  1262.     }
  1263.       }
  1264.     }
  1265.   }
  1266.  
  1267.   pix = XCreateBitmapFromData(dpy, rootwin, new, s*w, s*h);
  1268.   free(new); 
  1269.   return pix;      
  1270. }
  1271.  
  1272.  
  1273. Pixmap 
  1274. magnify_colrpix (img, s, bg_pix, mask)
  1275. Image *img;
  1276. int s;
  1277. Pixel bg_pix;
  1278. Pixmap mask;
  1279. {
  1280.   int i, j, r, ri, rc, c, l,
  1281.       rsize, rowbytesize, bytesize, rmask;
  1282.   char *dp, *rp, *rpsave, *data;
  1283.   Pixmap pixmap;
  1284.   Pixel index[256], pixel;
  1285.   XImage *ximage;
  1286.   GC gc;
  1287.   X11Image *ximg = (X11Image *) img->hook;
  1288.  
  1289.   if (!ximg || !img->rawcolrdata)  return None;
  1290.  
  1291.   /* find reverse index->pixel mapping */
  1292.   for (c=0; c<256; c++) {
  1293.     index[c] = XBlackPixel(dpy,screen);
  1294.   }
  1295.   for (c=0; c<img->numcolors; c++) {
  1296.     index[img->rawpalette[4*c]] = ximg->colpix[c];
  1297.   }
  1298.  
  1299.   /* make color data */
  1300.   rsize = img->pixelsize;
  1301.   rmask = (1<<img->pixelsize) - 1;
  1302.   rowbytesize = img->w*s * depth/8;
  1303.   bytesize = rowbytesize * img->h*s;
  1304.   data = xmalloc(bytesize*sizeof(char));
  1305.   dp = data;
  1306.   rp = img->rawcolrdata;
  1307.   for (r=0; r<img->h; r++) {
  1308.     rpsave = rp;
  1309.     for (j=0; j<s; j++) {
  1310.       rp = rpsave;
  1311.       ri = 8 - img->pixelsize;
  1312.       for (c=0; c<img->w; c++) {
  1313.     rc = ((int) (*rp>>ri)) & rmask;
  1314.     if (ri) {
  1315.       ri -= img->pixelsize;
  1316.     } else {
  1317.       ri = 8 - img->pixelsize;
  1318.       rp++;
  1319.     }
  1320.     pixel = index[rc];
  1321.     for (i=0; i<s; i++) {
  1322.       for (l=depth-8; l>=0; l-=8) {
  1323.         *dp = (pixel>>l) & 0xff;
  1324.         dp++;
  1325.       }
  1326.     }
  1327.       }
  1328.       if ((img->pixelsize*img->w)%8) {
  1329.     rp++;
  1330.       }
  1331.     }
  1332.   }
  1333.  
  1334.   /* convert to XImage */
  1335.   ximage = XCreateImage(dpy, DefaultVisual(dpy,screen), depth,
  1336.             ZPixmap, 0, data, img->w*s, img->h*s,
  1337.             8, rowbytesize);
  1338.   if (!ximage) {
  1339.     free(data);
  1340.     return None;
  1341.   }
  1342.   ximage->byte_order = MSBFirst;
  1343.   ximage->bitmap_bit_order = MSBFirst;
  1344.     
  1345.   /* and finally to Pixmap */
  1346.   pixmap = XCreatePixmap(dpy, rootwin, ximage->width, ximage->height, depth);
  1347.   if (!pixmap)  pixmap = None;
  1348.   if (pixmap == None) {
  1349.     XDestroyImage(ximage);
  1350.     /* XDestroyImage also frees data */
  1351.     return None;
  1352.   }
  1353.   gc = XCreateGC(dpy, pixmap, 0, NULL);
  1354.  
  1355.   /* background */
  1356.   XSetClipOrigin(dpy, gc, 0, 0);
  1357.   XSetForeground(dpy, gc, bg_pix);
  1358.   XFillRectangle(dpy, pixmap, gc, 0, 0, ximage->width, ximage->height);
  1359.  
  1360.   XSetClipMask(dpy, gc, mask);
  1361.   XPutImage(dpy, pixmap, gc, ximage, 0, 0, 0, 0,
  1362.         ximage->width, ximage->height);
  1363.   XFreeGC(dpy, gc);
  1364.   XDestroyImage(ximage);
  1365.   /* XDestroyImage also frees data */
  1366.  
  1367.   return pixmap;
  1368. }
  1369.  
  1370. /* actions */
  1371.  
  1372. void 
  1373. show_image (w, event, params, num_params)
  1374. Widget w;
  1375. XEvent *event;
  1376. String *params;
  1377. Cardinal *num_params;
  1378. {
  1379.   int i = shell_index(XtParent(XtParent(w)));
  1380.   FamilyStuff *fms = &family[i];
  1381.   ImageStuff *ims;
  1382.   char *add;
  1383.  
  1384.   sprintf(buffer, "%s: ", images[i]->name);
  1385.   if (fms->imagn) {
  1386.     sprintf(buffer+strlen(buffer)-2, " (x %d): ", magnif[fms->imagn]);
  1387.   }
  1388.   add = buffer + strlen(buffer);
  1389.   if (i>=numfamilies)  return;
  1390.   for (ims = fms->images; ims; ims = ims->next) {
  1391.     if (w == ims->mono_w) {
  1392.       sprintf(add, "%dx%d mono",  ims->w, ims->h);
  1393.     } else if (w == ims->mask_w) {
  1394.       sprintf(add, "%dx%d mask",  ims->w, ims->h);
  1395.     } else if (w == ims->comb_w) {
  1396.       sprintf(add, "%dx%d comb",  ims->w, ims->h);
  1397.     } else if (w == ims->colr_w) {
  1398.       sprintf(add, "%dx%d color", ims->w, ims->h);
  1399.     }
  1400.   }
  1401.   if (*add) {
  1402.     XtVaSetValues(fms->label, XtNlabel, buffer, NULL);
  1403.   }
  1404. }
  1405.  
  1406. void 
  1407. show_family (w, event, params, num_params)
  1408. Widget w;
  1409. XEvent *event;
  1410. String *params;
  1411. Cardinal *num_params;
  1412. {
  1413.   int i = shell_index(XtParent(XtParent(w)));
  1414.   FamilyStuff *fms = &family[i];
  1415.  
  1416.   if (fms->imagn) {
  1417.     sprintf(buffer, "%s (x %d)                   ",
  1418.         images[i]->name, magnif[fms->imagn]);
  1419.   } else {
  1420.     sprintf(buffer, "%s                   ",
  1421.         images[i]->name);
  1422.   }
  1423.   XtVaSetValues(fms->label, XtNlabel, buffer, NULL);
  1424. }
  1425.  
  1426. void 
  1427. image_action (w, event, params, num_params)
  1428. Widget w;
  1429. XEvent *event;
  1430. String *params;
  1431. Cardinal *num_params;
  1432. {
  1433.   int i = shell_index(XtParent(XtParent(w)));
  1434.   FamilyStuff *fms = &family[i];
  1435.  
  1436.   switch (event->type) {
  1437.   case ButtonPress:
  1438.     if (event->xbutton.button == 1) {
  1439.       if (fms->prep == prep_delete) {
  1440.     if (delete_image(w)) {
  1441.       if (empty_family(i)) {
  1442.         delete_family(i);
  1443.         return;
  1444.       } else {
  1445.         display_family(i, fms->imagn);
  1446.       }
  1447.     }
  1448.       } else if (fms->prep == prep_update) {
  1449.     update_or_export_image(w, 0);
  1450.       } else if (fms->prep == prep_export) {
  1451.     update_or_export_image(w, 1);
  1452.       }
  1453.       reset_prep(i);
  1454.     }
  1455.   }   
  1456. }
  1457.  
  1458. ImageStuff *
  1459. find_ims(fms,w)
  1460. FamilyStuff *fms;
  1461. Widget w;
  1462. {
  1463.   ImageStuff *ims;
  1464.  
  1465.   for (ims = fms->images; ims; ims = ims->next) {
  1466.     if (w == ims->mono_w) {
  1467.       return ims;
  1468.     } else if (w == ims->mask_w) {
  1469.       return ims;
  1470.     } else if (w == ims->comb_w) {
  1471.       return ims;
  1472.     } else if (w == ims->colr_w) {
  1473.       return ims;
  1474.     }
  1475.   }
  1476.   return NULL;
  1477. }
  1478.  
  1479. int
  1480. delete_image (w)
  1481. Widget w;
  1482. {
  1483.   int j, i;
  1484.   FamilyStuff *fms;
  1485.   ImageStuff *ims;
  1486.   Image *img;
  1487.   X11Image *ximg = NULL;
  1488.  
  1489.   if (!w)  return 0;
  1490.   i = shell_index(XtParent(XtParent(w)));
  1491.   fms = &family[i];
  1492.   ims = find_ims(fms,w);
  1493.   if (!ims)  return 0;
  1494.   img = find_image(images[i], ims->w, ims->h);
  1495.   if (!img)  return 0;
  1496.   ximg = (X11Image *) img->hook;
  1497.   if (!ximg)  return 0;
  1498.  
  1499.   if (w == ims->mono_w) {
  1500.     undisplay_family(i);
  1501.     /* do not free unmagnified pixmap; they might be used in the main window */
  1502.     ims->mono_p[0] = None;
  1503.     for (j=1; j<NUMMAGN; j++) {
  1504.       if (ims->mono_p[j] != None) {
  1505.     XFreePixmap(dpy, ims->mono_p[j]);
  1506.     ims->mono_p[j] = None;
  1507.       }
  1508.     }
  1509.     ximg->mono = None;
  1510.     ximg->monodata = NULL;
  1511.     img->rawmonodata = NULL;
  1512.     img->monodata = lispnil;
  1513.   } else if (w == ims->mask_w) {
  1514.     undisplay_family(i);
  1515.     ims->mask_p[0] = None;
  1516.     for (j=1; j<NUMMAGN; j++) {
  1517.       if (ims->mask_p[j] != None) {
  1518.     XFreePixmap(dpy, ims->mask_p[j]);
  1519.     ims->mask_p[j] = None;
  1520.       }
  1521.     }
  1522.     ximg->mask = None;
  1523.     ximg->maskdata = NULL;
  1524.     img->rawmaskdata = NULL;
  1525.     img->maskdata = lispnil;
  1526.   } else if (w == ims->colr_w) {
  1527.     undisplay_family(i);
  1528.     ims->colr_p[0] = None;
  1529.     for (j=1; j<NUMMAGN; j++) {
  1530.       if (ims->colr_p[j] != None) {
  1531.     XFreePixmap(dpy, ims->colr_p[j]);
  1532.     ims->colr_p[j] = None;
  1533.       }
  1534.     }
  1535.     ximg->colr = None;
  1536.     ximg->colrdata = NULL;
  1537.     img->rawcolrdata = NULL;
  1538.     img->colrdata = lispnil;
  1539.   } else if (w == ims->comb_w) {
  1540.     XBell(dpy,35);
  1541.     return 0;
  1542.   }
  1543.   if (w == ims->mono_w || w == ims->mask_w) {
  1544.     for (j=0; j<NUMMAGN; j++) {
  1545.       if (ims->comb_p[j] != None) {
  1546.     XFreePixmap(dpy, ims->comb_p[j]);
  1547.     ims->comb_p[j] = None;
  1548.       }
  1549.     }
  1550.   }
  1551.  
  1552.   return 1;
  1553. }
  1554.  
  1555. int
  1556. update_or_export_image (w, flag)
  1557. Widget w;
  1558. int flag;
  1559. {
  1560.     int i, ok = 0, imag;
  1561.     FamilyStuff *fms;
  1562.     ImageStuff *ims;
  1563.     Image *img;
  1564.     FILE *fp = NULL;
  1565.     ImageFamily *imf;
  1566.  
  1567.     if (!w)
  1568.       return 0;
  1569.     i = shell_index(XtParent(XtParent(w)));
  1570.     fms = &family[i];
  1571.     imf = images[i];
  1572.     ims = find_ims(fms, w);
  1573.     if (!ims)
  1574.       return 0;
  1575.     img = find_image(images[i], ims->w, ims->h);
  1576.     if (!img)
  1577.       return 0;
  1578.  
  1579.     if (outdirname && outdirname[0]) {
  1580.     sprintf(shortbuf, "%s/%s.%dx%d.",
  1581.         outdirname, imf->name, img->w, img->h);
  1582.     } else {
  1583.     sprintf(shortbuf, "%s.%dx%d.", imf->name, img->w, img->h);
  1584.     }
  1585.  
  1586.     if (w == ims->mono_w) {
  1587.     strcat(shortbuf, "b");
  1588.     if (flag) {
  1589.         if (img->rawmonodata && (fp = fopen(shortbuf, "w"))) {
  1590.         write_xbm_file(fp, imf->name, img->w, img->h, img->rawmonodata);
  1591.         ok = 1;
  1592.         }
  1593.     } else {
  1594.         if (read_xbm_file(shortbuf, imf, mark_changed)) {
  1595.         mark_changed(imf);
  1596.         ok = 1;
  1597.         }
  1598.     }
  1599.     } else if (w == ims->mask_w) {
  1600.     strcat(shortbuf, "m");
  1601.     if (flag) {
  1602.         if (img->rawmaskdata && (fp = fopen(shortbuf, "w"))) {
  1603.         write_xbm_file(fp, imf->name, img->w, img->h, img->rawmaskdata);
  1604.         ok = 1;
  1605.         }
  1606.     } else {
  1607.         if (read_xbm_file(shortbuf, imf, mark_changed)) {
  1608.         mark_changed(imf);
  1609.         ok = 1;
  1610.         }
  1611.     }
  1612.     } else  if (w == ims->colr_w) {
  1613.     strcat(shortbuf, "xpm");
  1614.     if (img->rawcolrdata && (fp = fopen(shortbuf, "w"))) {
  1615.         write_xpm_file(fp, imf->name, img);
  1616.         ok = 1;
  1617.     } else {
  1618.         if (read_xpm_file(shortbuf, imf, mark_changed)) {
  1619.         mark_changed(imf);
  1620.         ok = 1;
  1621.         }
  1622.     }
  1623.     } else {
  1624.     XBell(dpy,35);
  1625.     return 0;
  1626.     }
  1627.  
  1628.     if (fp)
  1629.       fclose(fp);
  1630.  
  1631.     if (ok && !flag) {
  1632.     imag = fms->imagn;
  1633.     undisplay_family(i);
  1634.     x11_interp_imf(dpy, rootwin, images[i], TRUE);
  1635.     img = best_image(images[i], basew, baseh);
  1636.     if (img->hook != NULL) {
  1637.         X11Image *ximg = (X11Image *) img->hook;
  1638.         if (ximg->colr != None) {
  1639.         XtVaSetValues(fms->image, XtNbitmap, ximg->colr, NULL);
  1640.         } else if (ximg->mono != None) {
  1641.         XtVaSetValues(fms->image, XtNbitmap, ximg->mono, NULL);
  1642.         } else {
  1643.         XtVaSetValues(fms->image, XtNbitmap, ximg->mask, NULL);
  1644.         }
  1645.     }
  1646.     display_family(i, imag);
  1647.     }
  1648.  
  1649.     if (ok) {
  1650.     sprintf(buffer, "%s \"%s\"", flag ? "exported in" : "updated from",
  1651.         shortbuf);
  1652.     } else {
  1653.     XBell(dpy,35);
  1654.     sprintf(buffer, "%s failed", flag ? "export" : "update");
  1655.     }
  1656.     XtVaSetValues(fms->label, XtNlabel, buffer, NULL);
  1657.     return ok;
  1658. }
  1659.  
  1660. Image *
  1661. find_image(imf, w, h)
  1662. ImageFamily *imf;
  1663. int w, h;
  1664. {
  1665.   Image *img;
  1666.  
  1667.   for (img = imf->images; img != NULL; img = img->next) {
  1668.     if (w == img->w && h == img->h) {
  1669.       return img;
  1670.     }
  1671.   }
  1672.   return NULL;
  1673. }
  1674.  
  1675. /* i>=0: delete family #i;  i<0: delete selected families */
  1676. void 
  1677. delete_family (i)
  1678. int i;
  1679. {
  1680.   int j, n, hidden;
  1681.   Boolean state;
  1682.  
  1683.   hidden = 0;
  1684.   n = 0;
  1685.   for (j=0; j<numfamilies; j++) {
  1686.     if (i<0) {
  1687.       XtVaGetValues(family[j].name, XtNstate, &state, NULL);
  1688.     }
  1689.     if ((i<0 && state) || (j==i)) {
  1690.       if (!hidden)  hide_image_families();
  1691.       hidden = 1;
  1692.       destroy_family(j);
  1693.     } else {
  1694.       family[n] = family[j];
  1695.       images[n] = images[j];
  1696.       n++;
  1697.     }
  1698.   }
  1699.  
  1700.   if (numfamilies>n) {
  1701.     sprintf(buffer, "%d image families left", n);
  1702.     XtVaSetValues(message, XtNlabel, buffer, NULL);
  1703.   }
  1704.   numfamilies = numimages = n;
  1705.   if (hidden) {
  1706.     cols = MINCOLS;
  1707.     show_image_families(FALSE);
  1708.   }
  1709. }
  1710.  
  1711. void 
  1712. destroy_family (i)
  1713. int i;
  1714. {
  1715.   FamilyStuff *fms = &family[i];
  1716.   ImageStuff *ims;
  1717.  
  1718.   for (ims = fms->images; ims; ims = ims->next) {
  1719.     delete_image(ims->mono_w);
  1720.     delete_image(ims->mask_w);
  1721.     delete_image(ims->colr_w);
  1722.   }
  1723.   family[i].shell = NULL;
  1724. }
  1725.  
  1726. int
  1727. empty_family(i)
  1728. int i;
  1729. {
  1730.   Image *img;
  1731.   X11Image *ximg;
  1732.  
  1733.   for (img = images[i]->images; img != NULL; img = img->next) {
  1734.     if (!img->hook)  continue;
  1735.     ximg = (X11Image *) img->hook;
  1736.     if (ximg->mono != None || ximg->colr != None || ximg->mask != None) {
  1737.       return 0;
  1738.     }
  1739.   }
  1740.  
  1741.   return 1;
  1742. }
  1743.